//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using OpenLS.Core;

namespace OpenLS.Spreadsheet.Expressions
{
    internal sealed class MultiCellsRangeReference : CellsRangeReference
    {
        private CellReference _bottomRight;
        private CellReference _topLeft;

        public MultiCellsRangeReference(CellReference topLeft, CellReference bottomRight)
        {
            Contract.Requires(topLeft != null);
            Contract.Requires(bottomRight != null);
            _topLeft = topLeft;
            _bottomRight = bottomRight;
            this.Worksheet.AddWeakRangeChanging(OnRangeChanging);
        }

        private void OnRangeChanging(object sender, RangeChangeEventArgs e)
        {
            if (this.Worksheet.Workbook.UndoManager.State == UndoState.Undo)
                return;
            if (e.Type == RangeChangeType.Deletion)
            {
                switch (e.Hint)
                {
                    case CellMoveOrientationHint.Vertical:
                        if (e.Address.Contains(TopLeft.Address))
                        {
                            int offset = e.Address.TopLeft.Row - _topLeft.Row.Anchor.Index;
                            offset = Math.Max(offset, -_topLeft.Row.Anchor.Index);
                            SetTopLeft(
                                _topLeft.Offset(
                                    new CellRelativeAddress(offset , 0)));
                            Contract.Assert((_topLeft.Row.Anchor.Index == e.Address.TopLeft.Row));
                        }
                        if (e.Address.Contains(this.BottomRight.Address))
                        {
                            int offset = e.Address.TopLeft.Row - _bottomRight.Row.Anchor.Index - 1;
                            offset = Math.Max(offset, -_bottomRight.Row.Anchor.Index);
                            SetBottomRight(
                                _bottomRight.Offset(
                                    new CellRelativeAddress(offset, 0)));
                        }
                        break;
                    case CellMoveOrientationHint.Horizontal:
                        {
                            if (e.Address.Contains(TopLeft.Address))
                            {
                                int offset = e.Address.TopLeft.Column - _topLeft.Column.Anchor.Index;
                                offset = Math.Max(offset, -_topLeft.Column.Anchor.Index);
                                SetTopLeft(
                                    _topLeft.Offset(
                                        new CellRelativeAddress(0, offset)));
                            }
                            if (e.Address.Contains(this.BottomRight.Address))
                            {
                                int offset = e.Address.TopLeft.Column - _bottomRight.Column.Anchor.Index - 1;
                                offset = Math.Max(offset, -_bottomRight.Column.Anchor.Index);
                                SetBottomRight(
                                    _bottomRight.Offset(
                                        new CellRelativeAddress(0, offset)));
                            }
                            break;
                            
                        }

                }
            }
        }

        public override CellReference TopLeft
        {
            get { return _topLeft; }

        }
        void SetTopLeft (CellReference topLeft)
        {
            this.Worksheet.Workbook.AddUndoAction(_topLeft, SetTopLeft);
            this._topLeft = topLeft;
        }
        void SetBottomRight(CellReference bottomRight)
        {
            this.Worksheet.Workbook.AddUndoAction(_bottomRight, SetBottomRight);
            this._bottomRight = bottomRight;
        }

        public override CellReference BottomRight
        {
            get { return _bottomRight; }
        }


        public override Worksheet Worksheet
        {
            get { return TopLeft.Cell.Worksheet; }
        }

        public override string Label
        {
            get
            {

                int i = compareReferences(TopLeft, BottomRight);
                if (i == 1)
                {
                    return string.Format("{1}:{0}", TopLeft.Label, BottomRight.Label);
                }
                return string.Format("{0}:{1}", TopLeft.Label, BottomRight.Label);
            }
        }

        private static int compareReferences(CellReference r1, CellReference r2)
        {
            int columnDelta = r1.Cell.Column.Index - r2.Cell.Column.Index;
            int rowDelta = r1.Cell.Row.Index - r2.Cell.Row.Index;
            if (columnDelta*rowDelta < 0)
                return 0;
            if (columnDelta < 0 || rowDelta < 0)
                return -1;
            if (rowDelta > 0 || columnDelta > 0)
                return 1;
            return 0;
        }

        protected override Range GetRange()
        {

            {
                return CreateRange();
            }
        }

        protected override Range CreateRange()
        {
            return Range.Create(Worksheet, new RangeAddress(TopLeft.Address, BottomRight.Address), false);
        }

        public override RangeReference Offset(CellRelativeAddress address)
        {
            if (address.IsZero)
                return this;
            return new MultiCellsRangeReference(TopLeft.Offset(address), BottomRight.Offset(address));
        }

        internal override RangeReference Transposed(Cell target)
        {
            return new MultiCellsRangeReference(TopLeft.Transposed(target), BottomRight.Transposed(target))
                ;
        }

        internal override RangeReference Projected(Worksheet targetWorksheet)
        {
            return new MultiCellsRangeReference(TopLeft.Projected(targetWorksheet),
                                                BottomRight.Projected(targetWorksheet));
        }
    }
}